﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace BanditGame2.Classes
{
        public delegate void MapChangedHandler(int iChanged, int jChanged);
        public class PathSolver
        {
            private int[,] _Map;
            private int _Rows;
            private int _Cols;
            private int _Path = 100;
            public event MapChangedHandler OnMapChangedEvent;

            /// <summary>
            /// Konstruktor vytvori prazdne pole, zdi jsou vytvoreny Indexerem
            /// </summary>
            /// <param name="rows"></param>
            /// <param name="cols"></param>
            public PathSolver(int rows, int cols)
            {
                _Map = new int[rows, cols];
                _Rows = rows;
                _Cols = cols;
            }
            
            /// <summary>
            ///Indexer - nastavuje hodnotu policek
            /// </summary>
            /// <param name="row"></param>
            /// <param name="col"></param>
            /// <returns></returns>
            public int this[int row, int col]
            {
                get { return _Map[row, col]; }
                set
                {
                    _Map[row, col] = value;
                    if (this.OnMapChangedEvent != null)	// trigger event
                        this.OnMapChangedEvent(row, col);
                }
            }

            //Metoda vraci obsah uzlu podle jeho cisla
            private int GetNodeContents(int[,] map, int nodeNo)
            {
                int cols = map.GetLength(1);
                return map[nodeNo / cols, nodeNo - nodeNo / cols * cols];
            }

            /// <summary>
            /// Metoda meni obsah uzlu
            /// </summary>
            /// <param name="map"></param>
            /// <param name="nodeNo"></param>
            /// <param name="newValue"></param>
            private void ChangeNodeContents(int[,] map, int nodeNo, int newValue)
            {
                int cols = map.GetLength(1);
                map[nodeNo / cols, nodeNo - nodeNo / cols * cols] = newValue;
            }

            /// <summary>
            /// Metoda vraci smer ktery souradnice prvniho pole nejkratsi cesty mezi 2 body
            /// pokud cesta neexistuje, vraci null
            /// </summary>
            /// <param name="fromY"></param>
            /// <param name="fromX"></param>
            /// <param name="toY"></param>
            /// <param name="toX"></param>
            /// <returns></returns>
            public int[] FindPath(int fromY, int fromX, int toY, int toX)
            {
                int beginningNode = fromY * _Cols + fromX;
                int endingNode = toY * _Cols + toX;
                int[,] mapSolved = Search(beginningNode, endingNode);
                if (mapSolved == null)
                {
                    return null;
                }
                else
                {
                    int[] field = new int[2];
                    if (mapSolved[fromY, fromX - 1] == 100)
                    {
                        field[0] = fromY;
                        field[1] = fromX - 1;
                        return field;
                    }
                    else if (mapSolved[fromY, fromX + 1] == 100)
                    {
                        field[0] = fromY;
                        field[1] = fromX + 1;
                        return field;
                    }
                    else if (mapSolved[fromY - 1, fromX] == 100)
                    {
                        field[0] = fromY - 1;
                        field[1] = fromX;
                        return field;
                    }
                    else if (mapSolved[fromY + 1, fromX] == 100)
                    {
                        field[0] = fromY + 1;
                        field[1] = fromX;
                        return field;
                    }
                    return null;
                }
            }
          
            /// <summary>
            /// Metoda k vypoctu nejkratsi vzdalenosti vychazejici z algoritmu vyhledani do sirky
            /// Priradi kazdemu uzlu (policku) jeho cislo a vola algoritmus
            /// </summary>
            private enum Status
            { Ready, Waiting, Processed }
            private int[,] Search(int start, int end)
            {
                const int empty = 0;

                int rows = _Rows;
                int cols = _Cols;
                int max = rows * cols;
                int[] queue = new int[max];
                int[] origin = new int[max];
                int front = 0, rear = 0;

                //kontrola zda start i end jsou volne policka na ktere lze vstoupit
                if (GetNodeContents(_Map, start) != empty || GetNodeContents(_Map, end) != empty)
                {
                    return null;
                }

                //vytvoreni pomocneho pole pro ulozeni stavu
                int[,] mapStatus = new int[rows, cols];
                //Na pocatku jsou vsechna policka ve stavu ready
                for (int i = 0; i < rows; i++)
                    for (int j = 0; j < cols; j++)
                        mapStatus[i, j] = (int)Status.Ready;

                //startovni uzel pridame do queue
                queue[rear] = start;
                origin[rear] = -1;
                rear++;
                int current, left, right, top, down;
                while (front != rear)	//dokud neni queue prazdna	
                {
                    if (queue[front] == end)		// nalezena cesta
                        break;

                    current = queue[front];

                    left = current - 1;
                    if (left >= 0 && left / cols == current / cols) 	//pokud levy uzel existuje
                        if (GetNodeContents(_Map, left) == empty) 	//pokud lze na levy uzel vstoupit a cesta existuje
                            if (GetNodeContents(mapStatus, left) == (int)Status.Ready)	//pokud je levy uzel ve stavu ready
                            {
                                queue[rear] = left; //pridej do queue
                                origin[rear] = current;
                                ChangeNodeContents(mapStatus, left, (int)Status.Waiting); //zmen status na waiting
                                rear++;
                            }

                    right = current + 1;
                    if (right < max && right / cols == current / cols) 	//pokud pravy uzel existuje
                        if (GetNodeContents(_Map, right) == empty) 	//...stejne jako vyse
                            if (GetNodeContents(mapStatus, right) == (int)Status.Ready)
                            {
                                queue[rear] = right;
                                origin[rear] = current;
                                ChangeNodeContents(mapStatus, right, (int)Status.Waiting);
                                rear++;
                            }

                    top = current - cols;
                    if (top >= 0) 	                //pokud horni uzel existuje
                        if (GetNodeContents(_Map, top) == empty) 	
                            if (GetNodeContents(mapStatus, top) == (int)Status.Ready)
                            {
                                queue[rear] = top;
                                origin[rear] = current;
                                ChangeNodeContents(mapStatus, top, (int)Status.Waiting);
                                rear++;
                            }

                    down = current + cols;
                    if (down < max) 	//pokud spodni uzel existuje
                        if (GetNodeContents(_Map, down) == empty)
                            if (GetNodeContents(mapStatus, down) == (int)Status.Ready)
                            {
                                queue[rear] = down;
                                origin[rear] = current;
                                ChangeNodeContents(mapStatus, down, (int)Status.Waiting);
                                rear++;
                            }
                    //Zmen stav uzlu na Processed
                    ChangeNodeContents(mapStatus, current, (int)Status.Processed);
                    front++;

                }

                //vytvor pole pro reseni
                int[,] mapSolved = new int[rows, cols];
                for (int i = 0; i < rows; i++)
                    for (int j = 0; j < cols; j++)
                        mapSolved[i, j] = _Map[i, j];
                
                //backtracking pro nalezeni nejkratsi trasy
                current = end;

                ChangeNodeContents(mapSolved, current, _Path);
                for (int i = front; i >= 0; i--)
                {
                    if (queue[i] == current)
                    {
                        current = origin[i];
                        if (current == -1)		// maze is solved
                            return (mapSolved);
                        ChangeNodeContents(mapSolved, current, _Path);
                    }
                }

                //neexistuje zadna cesta mezi 2 body
                return null;
            }
        }
}
